07 类与面向对象
面向对象编程用类来组织数据和行为。Python的类机制简洁灵活,支持继承、多态、私有变量,还有迭代器和生成器这两个强大特性。
一、定义类
1.1 基本语法
python
>>> class MyClass:
... """一个简单的类"""
... i = 12345 # 类变量
...
... def f(self): # 方法
... return 'hello world'
...
>>> x = MyClass()
>>> x.f()
'hello world'self是实例本身,调用方法时不需要传。
1.2 __init__构造方法
python
>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart # 实例变量
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r
3.0
>>> x.i
-4.51.3 实例变量和类变量
python
>>> class Dog:
... kind = 'canine' # 类变量,所有实例共享
...
... def __init__(self, name):
... self.name = name # 实例变量,每个实例独立
...
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind
'canine'
>>> d.name
'Fido'
>>> d.kind is e.kind
True类变量是共享的,实例变量是独立的。可变对象(列表、字典)做类变量要小心:
python
>>> class Dog:
... tricks = [] # 错误!所有实例共享同一个列表
...
... def __init__(self, name):
... self.name = name
...
... def add_trick(self, trick):
... self.tricks.append(trick)应该在__init__里初始化:
python
>>> class Dog:
... def __init__(self, name):
... self.name = name
... self.tricks = [] # 每个实例独立的列表
...
... def add_trick(self, trick):
... self.tricks.append(trick)二、继承
2.1 基本继承
python
>>> class Animal:
... def speak(self):
... return "..."
...
... class Dog(Animal):
... def speak(self):
... return "汪汪"
...
>>> d = Dog()
>>> d.speak()
'汪汪'2.2 isinstance()和issubclass()
python
>>> isinstance(d, Dog)
True
>>> isinstance(d, Animal)
True
>>> issubclass(Dog, Animal)
True2.3 多重继承
python
>>> class A:
... def hello(self):
... print("A")
...
>>> class B(A):
... def hello(self):
... print("B")
...
>>> class C(A):
... def hello(self):
... print("C")
...
>>> class D(B, C):
... pass
...
>>> d = D()
>>> d.hello()
BPython用C3线性化算法解析多重继承的顺序,可以用D.__mro__查看方法解析顺序。
三、私有变量
3.1 约定和名称改写
python
>>> class Mapping:
... def __init__(self, iterable):
... self.items_list = []
... self.__update(iterable)
...
... def update(self, iterable):
... for item in iterable:
... self.items_list.append(item)
...
... __update = update # 私有副本
...
>>> class MappingSubclass(Mapping):
... def update(self, keys, values):
... # 不会覆盖__update
... for item in zip(keys, values):
... self.items_list.append(item)__开头的名称会被改写为_类名__名称,避免和子类冲突。这不是真正的私有,只是名称改写。
3.2 约定
_开头:受保护的,外部可以访问但不推荐__开头:私有的,名称会被改写- 没有前缀:公开的
四、迭代器
4.1 迭代协议
python
>>> class Reverse:
... def __init__(self, data):
... self.data = data
... self.index = len(data)
...
... def __iter__(self):
... return self
...
... def __next__(self):
... if self.index == 0:
... raise StopIteration
... self.index -= 1
... return self.data[self.index]
...
>>> for char in Reverse('spam'):
... print(char, end=' ')
...
m a p s实现了__iter__()和__next__()的对象就是迭代器。for循环自动调用这两个方法。
4.2 StopIteration
__next__()在没有更多元素时抛出StopIteration,for循环捕获它并结束循环。
五、生成器
5.1 基本用法
生成器是创建迭代器的简单方式:
python
>>> def reverse(data):
... for index in range(len(data) - 1, -1, -1):
... yield data[index]
...
>>> for char in reverse('spam'):
... print(char, end=' ')
...
m a p syield让函数变成生成器函数,每次调用next()时执行到下一个yield。
5.2 生成器表达式
python
>>> sum(i*i for i in range(10)) # 求平方和
285
>>> list(i*i for i in range(10)) # 转成列表
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> unique_words = set(word for line in page for word in line.split())生成器表达式用圆括号,比列表推导式更省内存(按需生成,不一次性创建所有元素)。
六、魔术方法
6.1 常用魔术方法
python
>>> class Vector:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
... def __repr__(self):
... return f'Vector({self.x!r}, {self.y!r})'
...
... def __abs__(self):
... return (self.x ** 2 + self.y ** 2) ** 0.5
...
... def __add__(self, other):
... return Vector(self.x + other.x, self.y + other.y)
...
... def __mul__(self, scalar):
... return Vector(self.x * scalar, self.y * scalar)
...
... def __bool__(self):
... return bool(abs(self))
...
>>> v = Vector(3, 4)
>>> v
Vector(3, 4)
>>> abs(v)
5.0
>>> v + Vector(1, 2)
Vector(4, 6)
>>> v * 3
Vector(9, 12)6.2 常见魔术方法列表
| 方法 | 说明 |
|---|---|
__init__ | 构造方法 |
__repr__ | 字符串表示(给程序员看) |
__str__ | 字符串表示(给用户看) |
__len__ | len() |
__getitem__ | 索引访问 |
__setitem__ | 索引赋值 |
__contains__ | in运算符 |
__add__ | + |
__mul__ | * |
__eq__ | == |
__lt__ | < |
__bool__ | 布尔值 |
__iter__ | 迭代器 |
__next__ | 获取下一个元素 |
__enter__/__exit__ | with语句 |
__call__ | 函数调用 |
七、总结
| 概念 | 说明 |
|---|---|
| 类变量 | 所有实例共享 |
| 实例变量 | 每个实例独立 |
| 继承 | 子类继承父类 |
| 多重继承 | C3线性化解析顺序 |
__私有 | 名称改写,不是真私有 |
| 迭代器 | __iter__() + __next__() |
| 生成器 | yield关键字 |
| 生成器表达式 | 圆括号,按需生成 |
类变量用在不可变类型上,可变类型要在__init__里初始化。生成器比列表推导式省内存,适合大数据量场景。